#pragma once


#include "es.h"
#include "user_action.pb.h"
#include <algorithm>
#include <memory>
#include <string>
#include <unordered_map>

namespace lyt{


    #define es_blog_index "oj_blog_index"

    #define es_blog_id_key "blog_id"
    #define es_blog_title_key "blog_title"
    #define es_blog_date_key "blog_date"
    #define es_blog_userid_key "userid"
    #define es_blog_preview_key "preview"
    #define es_blog_vector_key "embedding"

    class EsBlogData{
    public:
        using ptr = std::shared_ptr<EsBlogData>;
        EsBlogData(const lyt::esclient::ptr& client)
            :_client(client)
        {}
        
        //  message KafkaCreateBlogInfo{
        //      string blog_id = 1;
        //      string title = 2;
        //      string date = 3;
        //      string user_id = 4;
        //      string preview = 5;
        // }
        bool index() {
            return lyt::index(_client)
                .add_keyword_field(es_blog_index)
                .add_text_field(es_blog_title_key)
                .add_text_field(es_blog_date_key)
                .add_text_field(es_blog_userid_key)
                .add_text_field(es_blog_preview_key)
                .add_dense_vector_384_field(es_blog_vector_key)
                .run(es_blog_index);
        }

        bool insert(const analytics::KafkaCreateBlogInfo& binfo, const std::vector<double>& embedding) {
            return lyt::insert(_client)
                .set_value(es_blog_id_key,binfo.blog_id())
                .set_value(es_blog_title_key,binfo.title())
                .set_value(es_blog_userid_key,binfo.user_id())
                .set_value(es_blog_preview_key,binfo.preview())
                .set_value(es_blog_date_key,binfo.date())
                .set_value(es_blog_vector_key,embedding)
                .run(es_blog_index, binfo.blog_id());
        }
        bool remove(const std::string& id) {
            return _client->remove_data(es_blog_index, id);
        }
        // 通过 向量查找
        bool search(const std::vector<double>& embedding,std::vector<analytics::KafkaCreateBlogInfo>& binfo,size_t size) {
            auto ret =  lyt::search(_client)
                .exclude_field(es_blog_vector_key)
                .set_size(size)
                .vector_search(embedding,es_blog_vector_key)
                .run(es_blog_index);
            binfo.clear();
            int rs = ret.first.size();
            for (int i = 0; i < rs; i++) {
                analytics::KafkaCreateBlogInfo info;
                info.set_blog_id(ret.first[i]["_source"][es_blog_id_key].asString());
                info.set_title(ret.first[i]["_source"][es_blog_title_key].asString());
                info.set_date(ret.first[i]["_source"][es_blog_date_key].asString());
                info.set_user_id(ret.first[i]["_source"][es_blog_userid_key].asString());
                info.set_preview(ret.first[i]["_source"][es_blog_preview_key].asString());
                binfo.push_back(std::move(info));
            }
            return true;
        }
        // 通过关键字搜索文章
        bool search(const std::string& key,std::vector<analytics::KafkaCreateBlogInfo>& binfo) {
            auto ret =  lyt::search(_client)
                .exclude_field(es_blog_vector_key)
                .add_should_match(es_blog_title_key, key)
                .add_should_match(es_blog_preview_key, key)
                .run(es_blog_index);
            binfo.clear();
            int rs = ret.first.size();
            for (int i = 0; i < rs; i++) {
                analytics::KafkaCreateBlogInfo info;
                info.set_blog_id(ret.first[i]["_source"][es_blog_id_key].asString());
                info.set_title(ret.first[i]["_source"][es_blog_title_key].asString());
                info.set_date(ret.first[i]["_source"][es_blog_date_key].asString());
                info.set_user_id(ret.first[i]["_source"][es_blog_userid_key].asString());
                info.set_preview(ret.first[i]["_source"][es_blog_preview_key].asString());
                binfo.push_back(std::move(info));
            } 
            return true;
        }
        // 通过id查找
        // 如果被文章被删除了，这个id可能还存在，所以可能会查不出来，查出来的文章个数少于id数应该看作正常现象
        bool search(const std::vector<std::string>& ids,std::unordered_map<std::string, std::vector<double>>& vectors) {
            auto ret =  lyt::search(_client)
                .exclude_field(es_blog_title_key)
                .exclude_field(es_blog_date_key)
                .exclude_field(es_blog_userid_key)
                .exclude_field(es_blog_preview_key)
                .add_must_terms_text(es_blog_id_key, ids)
                .run(es_blog_index);
            vectors.clear();
            int rs = ret.first.size();
            for (int i = 0; i < rs; i++) {
                auto& binfo = ret.first[i]["_source"];
                std::string id = binfo[es_blog_userid_key].asString();
                auto& id_v = vectors[id];
                int ds = binfo[es_blog_vector_key].size();
                for (int j = 0; j < ds; j++) {
                    id_v.push_back(binfo[es_blog_vector_key][i].asDouble());
                }
            } 
            return true;
        }

        // 通过id查找title
        // 如果被文章被删除了，这个id可能还存在，所以可能会查不出来，查出来的文章个数少于id数应该看作正常现象
        bool search(const std::vector<std::string>& ids,std::vector<std::string>& titles) {
            auto ret =  lyt::search(_client)
                .exclude_field(es_blog_userid_key)
                .exclude_field(es_blog_id_key)
                .exclude_field(es_blog_date_key)
                .exclude_field(es_blog_userid_key)
                .exclude_field(es_blog_preview_key)
                .add_must_terms_text(es_blog_id_key, ids)
                .run(es_blog_index);
            titles.clear();
            int rs = ret.first.size();
            for (int i = 0; i < rs; i++) {
                titles.push_back(ret.first[i]["_source"][es_blog_title_key].asString());
            } 
            return true;
        }
        

    private:
        esclient::ptr _client;
    };
}